#include "DrawUtils.h"

void CAnimator::ConstructL(CCoeControl* aControl, TInt aFrameRate)
	{
	CTimer::ConstructL();
	iFrameRate = aFrameRate;
	iControl = aControl;
	}

CAnimator::CAnimator() : CTimer(EPriorityIdle)
	{
	CActiveScheduler::Add(this);
	}

CAnimator::~CAnimator()
	{
	iAnimations.Close();
	}

const RArray<MAnimation*>& CAnimator::Animations() const
{
	return iAnimations;
}
TInt64 CAnimator::FrameTime() // in microseconds
{
	return iCurrentTime.MicroSecondsFrom(iStartTime).Int64();
}
void CAnimator::AppendL(MAnimation* aAnimation)
	{
	aAnimation->iAnimator = this;
	iAnimations.AppendL(aAnimation);
	}

void CAnimator::StartL()
	{
	iStartTime.UniversalTime();
	Cancel();
	After( 1000000/(TInt64)iFrameRate );
	}

void CAnimator::Stop()
	{
	Cancel();
	}


void CAnimator::RunL()
	{
	Cancel();
	After( 1000000/(TInt64)iFrameRate );
	iCurrentTime.UniversalTime();
	iControl->DrawDeferred();
	TInt i;
	for(i = 0 ; i < iAnimations.Count() && iAnimations[i]->IsDoneL() ; ++i);
	if(i == iAnimations.Count())
		Cancel();
	}

TMarqueeAnimation::TMarqueeAnimation()
	{
	iSpeed = 0;
	iExcessWidth = 0;
	}

void TMarqueeAnimation::ResetL(const TDesC& aText, const CFont& aFont, const TInt aTextMargin, const TRect& aRect)
	{
	CFont::TMeasureTextOutput measureTextOutput;
	TInt excessWidth;
	TInt charsDrawn = aFont.TextCount(aText, aRect.Width() - aTextMargin, excessWidth);
	if(charsDrawn != aText.Length())
		{
//		aFont.MeasureText(aText, NULL, &measureTextOutput);
//		iExcessWidth = measureTextOutput.iBounds.Width() + aTextMargin - aRect.Width() + 2;
		iExcessWidth = aFont.TextWidthInPixels(aText) + aTextMargin - aRect.Width();
		iSpeed = aFont.CharWidthInPixels(' ') * 10; // 1 char every 10th of seconds
		}
	else
		iSpeed = 0;
	}
TInt TMarqueeAnimation::OffsetL()
	{
	if(!iSpeed) return 0;
	TInt offset = iAnimator->FrameTime()*iSpeed / 1000000;
	if(offset >= iExcessWidth)
		{
		TInt timeSinceCompletion = (offset - iExcessWidth)/iSpeed; // in seconds
		if(timeSinceCompletion >= 1)
			return 0;
		else
			return -iExcessWidth;
		}
	return -offset;
	}
TBool TMarqueeAnimation::IsDoneL() const
	{
	if(!iSpeed) return ETrue;
	TInt offset = iAnimator->FrameTime()*iSpeed / 1000000;
	TInt timeSinceCompletion = (offset - iExcessWidth)/iSpeed; // in seconds
	return (offset >= iExcessWidth && timeSinceCompletion >= 1);
	}



void DrawTextExt(
		CWindowGc* aGc,
		const TRect& aRect, TSize aRoundedRectEllipse,
		const TDesC& aText, const CFont* aFont, const TRgb& aTextColor,
		CGraphicsContext::TTextAlign aTextAlign, TInt aTextMargin, TBool aCrop,
		const CGraphicsContext::TBrushStyle& aBackgroundBrushStyle, const TRgb& aBackgroundColor,
		const CGraphicsContext::TPenStyle& aBackgroundPenStyle, const TRgb& aBackgroundPenColor)
	{
	const TInt baseline = (aRect.Height() + aFont->AscentInPixels()) / 2;
	if(aBackgroundPenStyle != CGraphicsContext::ENullPen || aBackgroundBrushStyle != CGraphicsContext::ENullBrush)
		{
		aGc->SetPenColor(aBackgroundPenColor);
		aGc->SetPenStyle(aBackgroundPenStyle);
		aGc->SetBrushStyle(aBackgroundBrushStyle);
		aGc->SetBrushColor(aBackgroundColor);
		if(aRoundedRectEllipse == TSize())
			aGc->DrawRect(aRect);
		else
			aGc->DrawRoundRect(aRect, aRoundedRectEllipse);
		}
	
	aGc->UseFont(aFont);
	aGc->SetPenColor(aTextColor);
	aGc->SetBrushStyle(CGraphicsContext::ENullBrush);
	TInt excessWidth;
	TInt charsDrawn = aFont->TextCount(aText, aRect.Width() - aTextMargin, excessWidth);
	if(charsDrawn != aText.Length() && aCrop)
		{
		TInt clippedChars, excessWidthAfterClippingChars;
		TInt ellipsisWidth = aFont->CharWidthInPixels('\x2026');
		for(clippedChars = 0, excessWidthAfterClippingChars = excessWidth ; clippedChars < charsDrawn && excessWidthAfterClippingChars < ellipsisWidth ; ++clippedChars)
			excessWidthAfterClippingChars += aFont->CharWidthInPixels(aText[charsDrawn - 1 - clippedChars]);
		
		aGc->DrawText(aText.Left(charsDrawn - clippedChars), aRect, baseline, CGraphicsContext::ELeft, aTextMargin);
		TRect rect = aRect;
		rect.Move(aRect.Width() - excessWidthAfterClippingChars, 0);
		aGc->DrawText(_L("\x2026"), rect, baseline); //ellipsis
		}
	else if(charsDrawn != aText.Length())
		aGc->DrawText(aText, aRect, baseline, CGraphicsContext::ELeft, aTextMargin);
	else
		aGc->DrawText(aText, aRect, baseline, aTextAlign, aTextMargin);
	aGc->DiscardFont();
	}


void DrawMarqueeText(
		CWindowGc* aGc, const CFont* aFont,
		const TDesC& aText,
		const TRect& aRect, 
		TInt aBaseline,
		CGraphicsContext::TTextAlign aTextAlign,
		TInt aMargin,
		const TTimeIntervalMicroSeconds& aFrameTime, TBool& aMarqueeCompleted)
	{
	aMarqueeCompleted = EFalse;
	const TInt marqueeSpeed = aFont->CharWidthInPixels(' ') * 10; // pixels per seconds
	const TInt overflowWidth = aFont->TextWidthInPixels(aText) + aMargin - aRect.Width();
	
	TInt64 timeEndMarquee = overflowWidth * 1000000 / marqueeSpeed;
	TInt marginOffset;
	if(aFrameTime <= timeEndMarquee)
		marginOffset = - marqueeSpeed * aFrameTime.Int64() / 1000000;
	else if(timeEndMarquee >= 0 && aFrameTime <= timeEndMarquee + 1000000)
		marginOffset = - overflowWidth;
	else
		{
		marginOffset = 0;
		aMarqueeCompleted = ETrue;
		}
	
	aGc->UseFont(aFont);
	if(marginOffset)
		aGc->DrawText(aText, aRect, aBaseline, CGraphicsContext::ELeft, aMargin + marginOffset);
	else if(overflowWidth > 0)
		{
		TInt excessWidth;
		TInt charsDrawn = aFont->TextCount(aText, aRect.Width() - aMargin, excessWidth);
		TInt clippedChars, excessWidthAfterClippingChars;
		TInt ellipsisWidth = aFont->CharWidthInPixels('\x2026');
		for(clippedChars = 0, excessWidthAfterClippingChars = excessWidth ; clippedChars < charsDrawn && excessWidthAfterClippingChars < ellipsisWidth ; ++clippedChars)
			excessWidthAfterClippingChars += aFont->CharWidthInPixels(aText[charsDrawn - 1 - clippedChars]);
		aGc->DrawText(aText.Left(charsDrawn - clippedChars), aRect, aBaseline, CGraphicsContext::ELeft, aMargin);
		TRect rect = aRect;
		rect.Move(aRect.Width() - excessWidthAfterClippingChars, 0);
		aGc->DrawText(_L("\x2026"), rect, aBaseline, CGraphicsContext::ELeft); //ellipsis
		}
	else
		aGc->DrawText(aText, aRect, aBaseline, aTextAlign, aMargin);
	aGc->DiscardFont();
	}


void DrawMarqueeGeneric(
		CWindowGc* aGc, const CFont* aFont,
		TInt aWidth,
		const TRect& aRect, 
		TInt aBaseline,
		CGraphicsContext::TTextAlign aTextAlign,
		TInt aMargin,
		const TTimeIntervalMicroSeconds& aFrameTime,
		TInt& aOffset, TRect& aDrawRect, TBool& aMarqueeCompleted)
	{
	aMarqueeCompleted = EFalse;
	const TInt marqueeSpeed = aFont->CharWidthInPixels(' ') * 10; // pixels per seconds
	const TInt overflowWidth = aWidth + aMargin - aRect.Width();
	
	TInt64 timeEndMarquee = overflowWidth * 1000000 / marqueeSpeed;
	TInt marginOffset;
	if(aFrameTime <= timeEndMarquee)
		marginOffset = - marqueeSpeed * aFrameTime.Int64() / 1000000;
	else if(timeEndMarquee >= 0 && aFrameTime <= timeEndMarquee + 1000000)
		marginOffset = - overflowWidth;
	else
		{
		marginOffset = 0;
		aMarqueeCompleted = ETrue;
		}
	
	aGc->UseFont(aFont);
	if(marginOffset)
		{
		aOffset = aMargin + marginOffset;
		aDrawRect = aRect;
		}
	else if(overflowWidth > 0)
		{
		TInt ellipsisWidth = aFont->CharWidthInPixels('\x2026');
		aOffset = aMargin;
		aDrawRect = aRect;
		aDrawRect.iBr.iX -= ellipsisWidth;
		TRect rect = aRect;
		rect.iTl.iX = rect.iBr.iX - ellipsisWidth;
		aGc->UseFont(aFont);
		aGc->DrawText(_L("\x2026"), rect, aBaseline, CGraphicsContext::ELeft); //ellipsis
		aGc->DiscardFont();
		}
	else
		{
		aDrawRect = aRect;
		switch(aTextAlign)
			{
			case CGraphicsContext::ELeft:
				aOffset = aMargin;
				break;
			case CGraphicsContext::ECenter:
				aOffset = aMargin + (aRect.Width() - aWidth)/2;
				break;
			}
		}
	}

void SplitRectHorz(const TRect& aRect, TInt aWidth1, TRect& aCol1, TRect& aCol2)
	{
	TInt width1 = (aWidth1 >= 0) ? aWidth1 : aRect.Width() + aWidth1;
	aCol1.SetRect(aRect.iTl.iX, aRect.iTl.iY, aRect.iTl.iX + width1, aRect.iBr.iY);
	aCol2.SetRect(aRect.iTl.iX + width1, aRect.iTl.iY, aRect.iBr.iX, aRect.iBr.iY);
	}

void SplitRectHorz(const TRect& aRect, TInt aWidth1, TInt aWidth2, TRect& aCol1, TRect& aCol2, TRect& aCol3)
	{
	TInt width1 = (aWidth1 >= 0) ? aWidth1 : aRect.Width() + aWidth1;
	aCol1.SetRect(aRect.iTl.iX, aRect.iTl.iY, aRect.iTl.iX + width1, aRect.iBr.iY);
	TInt width2 = (aWidth2 >= 0) ? aWidth2 : aRect.Width() - width1 + aWidth2;
	aCol2.SetRect(aRect.iTl.iX + width1, aRect.iTl.iY, aRect.iTl.iX + width1 + width2, aRect.iBr.iY);
	aCol3.SetRect(aRect.iTl.iX + width1 + width2, aRect.iTl.iY, aRect.iBr.iX, aRect.iBr.iY);
	}

void SplitRectVert(const TRect& aRect, TInt aHeight1, TRect& aLine1, TRect& aLine2)
	{
	TInt height1 = (aHeight1 >= 0) ? aHeight1 : aRect.Height() + aHeight1;
	aLine1.SetRect(aRect.iTl.iX, aRect.iTl.iY, aRect.iBr.iX, aRect.iTl.iY + height1);
	aLine2.SetRect(aRect.iTl.iX, aRect.iTl.iY + height1, aRect.iBr.iX, aRect.iBr.iY);
	}
void SplitRectVert(const TRect& aRect, TInt aHeight1, TInt aHeight2, TRect& aLine1, TRect& aLine2, TRect& aLine3)
	{
	TInt height1 = (aHeight1 >= 0) ? aHeight1 : aRect.Height() + aHeight1;
	aLine1.SetRect(aRect.iTl.iX, aRect.iTl.iY, aRect.iBr.iX, aRect.iTl.iY + height1);
	TInt height2 = (aHeight2 >= 0) ? aHeight2 : aRect.Height() - height1 + aHeight2;
	aLine2.SetRect(aRect.iTl.iX, aRect.iTl.iY + height1, aRect.iBr.iX, aRect.iTl.iY + height1 + height2);
	aLine3.SetRect(aRect.iTl.iX, aRect.iTl.iY + height1 + height2, aRect.iBr.iX, aRect.iBr.iY);
	}
void ShrinkToSize(TRect& aRect, TInt aWidth, TInt aHeight)
	{
	aRect.Shrink(
			(aRect.Width() - aWidth)/2,
			(aRect.Height() - aHeight)/2
			);
	}




void CItemDrawerHelper::StartDrawItem(TInt aItemIndex, TInt aItemIsCurrent)
{
	iItemIndex = aItemIndex;
	iItemIsCurrent = aItemIsCurrent;
}

void CItemDrawerHelper::EndDrawItem()
	{
    if(iItemIsCurrent && iLastCurrentItem != iItemIndex)
    	{
		iAnimator->StartL();
		iLastCurrentItem = iItemIndex;
    	}
	}

void CItemDrawerHelper::DrawMarqueeItemPart(
		TInt aPartNumber,
		CWindowGc* aGc,
		const TRect& aRect, TSize aRoundedRectEllipse,
		const TDesC& aText, const CFont* aFont, const TRgb& aTextColor,
		CGraphicsContext::TTextAlign aTextAlign, TInt aTextMargin,
		const CGraphicsContext::TBrushStyle& aBackgroundBrushStyle, const TRgb& aBackgroundColor,
		const CGraphicsContext::TPenStyle& aBackgroundPenStyle, const TRgb& aBackgroundPenColor)
	{
	if(iItemIsCurrent && iLastCurrentItem != iItemIndex)
		iMarqueeAnimation[aPartNumber].ResetL(aText, *aFont, aTextMargin, aRect);
	DrawTextExt(aGc,
			aRect, aRoundedRectEllipse, 
			aText, aFont, aTextColor, aTextAlign, !iItemIsCurrent ? aTextMargin : aTextMargin + iMarqueeAnimation[aPartNumber].OffsetL(), !iItemIsCurrent || !iAnimator->IsActive(),
			aBackgroundBrushStyle, aBackgroundColor,
			aBackgroundPenStyle, aBackgroundPenColor);
	}

void CItemDrawerHelper::ConstructL(CCoeControl* aControl, TInt aCount)
	{
	iLastCurrentItem = KErrNone;
	iAnimator = new (ELeave) CAnimator;
	iMarqueeAnimation = new (ELeave) TMarqueeAnimation[aCount];
	iAnimator->ConstructL(aControl, 30);
	for(TInt i = 0 ; i < aCount ; ++i)
		iAnimator->AppendL(&iMarqueeAnimation[i]);
	
	}

